package com.example.oauth2.config;

import com.example.oauth2.component.PasswordAuthenticationConverter;
import com.example.oauth2.component.PasswordAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author <a href="nav1cat@outlook.com">nav1c</a>
 * @apiNote
 * @date 2023-12-18 14:57:48
 * @project study-cloud
 * @kit IntelliJ IDEA
 */
@Configuration
public class AuthenticationServerConfig {

    private static final int AUTHORIZATION_SERVER_FILTER_ORDER = ResourceServerConfig.RESOURCE_SERVER_FILTER_ORDER - 1;

    private final RegisteredClientRepository registeredClientRepository;

    private final OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService;

    private final OAuth2AuthorizationService oAuth2AuthorizationService;

    private final OAuth2TokenGenerator<? extends OAuth2Token> oAuth2TokenGenerator;

    private final PasswordEncoder passwordEncoder;

    private final UserDetailsService userDetailsService;

    @Autowired
    public AuthenticationServerConfig(RegisteredClientRepository registeredClientRepository,
                                      OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService,
                                      OAuth2AuthorizationService oAuth2AuthorizationService,
                                      OAuth2TokenGenerator<? extends OAuth2Token> oAuth2TokenGenerator,
                                      PasswordEncoder passwordEncoder,
                                      UserDetailsService userDetailsService) {
        this.oAuth2AuthorizationService = oAuth2AuthorizationService;
        this.registeredClientRepository = registeredClientRepository;
        this.oAuth2AuthorizationConsentService = oAuth2AuthorizationConsentService;
        this.oAuth2TokenGenerator = oAuth2TokenGenerator;
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * <p>
     *     想看遍历了哪些过滤器可以debug {@link org.springframework.web.filter.OncePerRequestFilter#doFilterInternal}
     * </p>
     * <p>
     * 入口 {@link AuthenticationManager#authenticate(Authentication)}<br>
     * endpontsMatcher ---> {@link OAuth2AuthorizationServerConfigurer#init(HttpSecurity)}<br>
     * </p>
     * <p>
     *     {@link org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter} 包含 <br>
     *    Ant [pattern='/oauth2/token', POST],Ant [pattern='/oauth2/introspect', POST],Ant [pattern='/oauth2/revoke', POST],Ant [pattern='/oauth2/device_authorization', POST]
     * </p>
     * <p>
     *     登录接口 /login --->
     *     {@link org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter}
     *     获取授权码:
     *     /oauth2/authorization  -->
     *     {@link org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter}<br>
     *     /oauth2/device_verification   --> {@link org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceVerificationEndpointFilter}<br>
     *     /oauth2/device_authorization  --> {@link org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceAuthorizationEndpointFilter}<br>
     *     获取token:
     *     /oauth2/token -->
     *     先客户端认证:
     *     {@link org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter}
     *     {@link org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter}<br>
     *     核验token:
     *     /oauth2/introspect   --> {@link org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter}<br>
     *     撤回token:
     *    /oauth2/revoke    --> {@link org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter}<br>
     *    /**
     *    /oauth2/jwks  --> {@link org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter}<br>
     *     /.well-known/oauth-authorization-server   --> {@link org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter}  查看授权服务器信息<br>
     * </p>
     * @param http HttpSecurity
     * @return SecurityFilterChain
     * @throws Exception Exception
     */
    @Bean
    @Order(AUTHORIZATION_SERVER_FILTER_ORDER)
    public SecurityFilterChain authenticationServerFilterChain(HttpSecurity http) throws Exception {
        final OAuth2AuthorizationServerConfigurer configurer = new OAuth2AuthorizationServerConfigurer();
        http.apply(configurer); //必须先apply 才能进行下面的设置
        configurer
                .registeredClientRepository(registeredClientRepository) //客户端存储器
                .authorizationService(oAuth2AuthorizationService)// 用于管理新的和现有的授权
                .authorizationConsentService(oAuth2AuthorizationConsentService)// 用于管理新的和现有的授权许可。
                .tokenGenerator(oAuth2TokenGenerator)// 用于生成 OAuth2 授权服务器支持的令牌。
//                .clientAuthentication(clientAuthentication())// OAuth2 客户端身份认证的配置器。
//                .authorizationEndpoint(authorizationEndpoint())// OAuth2 授权端点的配置器。
                .tokenEndpoint(
                        configurer1 -> configurer1.accessTokenRequestConverter(PasswordAuthenticationConverter.getInstance())
                                        .authenticationProvider(PasswordAuthenticationProvider.getInstance(oAuth2AuthorizationService,
                                                oAuth2TokenGenerator, userDetailsService, passwordEncoder))
                );// OAuth2 token 端点的配置器。
//                .tokenIntrospectionEndpoint(tokenIntrospectionEndpoint()) // OAuth2 token 内省端点的配置器。
//                .tokenRevocationEndpoint(Customizer.withDefaults());// OAuth2 token 撤销端点的配置器。

//        http.userDetailsService(userDetailsService);//这个可以不配置，容器可以找到它的bean
//        http.sessionManagement(httpSecuritySessionManagementConfigurer ->
//                httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));//登录页面401

        //将Oauth2的所有endpoint交给这个过滤器管理
        http.securityMatchers(matcherConfigurer -> matcherConfigurer
                        .requestMatchers(configurer.getEndpointsMatcher())
                        .requestMatchers("/login", "/logout"))
                .authorizeHttpRequests(registry -> registry.requestMatchers("/login", "/logout").permitAll()
                                .anyRequest().authenticated());

        http.csrf(AbstractHttpConfigurer::disable);
        http.formLogin(Customizer.withDefaults());
//        http.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED));//NOSONAR
        return http.build();
    }

    /**
     * {@link  AuthorizationGrantType#REFRESH_TOKEN} --> {@link org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter}<br>
     *                                                --> #{@link  OAuth2RefreshTokenAuthenticationToken} <br>
     * {@link AuthorizationGrantType#AUTHORIZATION_CODE} --> {@link org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter}<br>
     *                                                  --> {@link OAuth2AuthorizationCodeAuthenticationToken}<br>
     * {@link AuthorizationGrantType#CLIENT_CREDENTIALS} --> {@link org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter}<br>
     *                                                   -->{@link OAuth2ClientCredentialsAuthenticationToken}
     * {@link AuthorizationGrantType#DEVICE_CODE} --> {@link org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceCodeAuthenticationConverter}<br>
     *                                                   -->{@link OAuth2DeviceCodeAuthenticationToken}
     * {@link AuthorizationGrantType#PASSWORD} --> 可能已经不支持密码模式
     * @return {@link OAuth2TokenClaimsContext}
     */

}
